﻿using ScottPlot;
using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Linq;
using UtilZ.Dotnet.Ex.Base;
using UtilZ.Dotnet.Ex.Model;

namespace UtilZ.Dotnet.ScottPlotEx
{
    /// <summary>
    /// 瀑布图
    /// </summary>
    unsafe internal partial class WaterfallPlot_bk : IPlottable, IHasColorAxis
    {
        private int _dataHeight;
        /// <summary>
        /// 数据高度
        /// </summary>
        public int DataHeight
        {
            get { return this._dataHeight; }
        }

        private int _dataWidth;
        /// <summary>
        /// 数据宽度
        /// </summary>
        public int DataWidth
        {
            get { return this._dataWidth; }
        }


        private ScottPlot.Range _dataRange;
        /// <summary>
        /// 频谱图中的最小值
        /// </summary>
        public double Min
        {
            get { return this._dataRange.Min; }
        }

        /// <summary>
        /// 频谱图中的最大值
        /// </summary>
        public double Max
        {
            get { return this._dataRange.Max; }
        }

        private IColormap _colormap = null;
        /// <summary>
        /// 获取或设置颜色域
        /// </summary>
        public IColormap Colormap
        {
            get { return this._colormap; }
        }

        /// <summary>
        /// 获取数据范围
        /// </summary>
        /// <returns></returns>
        public ScottPlot.Range GetRange()
        {
            return this._dataRange;
        }

        private PlotMoveDirection _moveOrientation = PlotMoveDirection.Down;
        /// <summary>
        /// 移动方向
        /// </summary>
        public PlotMoveDirection MoveOrientation
        {
            get { return this._moveOrientation; }
        }

        private WaterfallPlotOptions _options = null;
        /// <summary>
        /// 可选项参数
        /// </summary>
        public WaterfallPlotOptions Options
        {
            get { return this._options; }
        }



        /// <summary>
        /// 垂直翻转
        /// </summary>
        private bool _flipVertically = false;

        /// <summary>
        /// 水平翻转
        /// </summary>
        private bool _flipHorizontally = false;

        /// <summary>
        /// Nan值点的颜色
        /// </summary>
        private ScottPlot.Color _naNCellColor = Colors.Transparent;

        private bool _cacheOriginValue = false;
        private readonly UnmanagedMemoryPointer<double> _intensities = new UnmanagedMemoryPointer<double>();
        private readonly UnmanagedMemoryPointer<uint> _argbsPointer = new UnmanagedMemoryPointer<uint>();
        private int _argsbWriteOffset = 0;
        private int _imgPixs = 0;

        private bool _argbsIsUpdated = false;
        /// <summary>
        /// 少一行的元素个数
        /// </summary>
        private int _lessLineEleCount = 0;

        private SKPaint _paint = null;
        private SKBitmap _bitmap = null;
        private SKImageInfo _imageInfo;
        private CoordinateRect _alignedExtent;
        private double _cellWidth;
        private double _cellHeight;

        private SKBitmap _bitmap2 = null;
        private SKCanvas _bitmap2Canvas = null;


        /// <summary>
        /// 
        /// </summary>
        public WaterfallPlot_bk()
        {

        }

        /// <summary>
        /// 
        /// </summary>
        ~WaterfallPlot_bk()
        {
            this._bitmap?.Dispose();
            this._intensities.Dispose();
            this._argbsPointer.Dispose();
            this._paint?.Dispose();

            this._bitmap2?.Dispose();
            this._bitmap2Canvas?.Dispose();
        }



        /// <summary>
        /// 设置瀑布图
        /// </summary>
        /// <param name="dataWidth">数据宽带(移动方向的垂直方向时此值为FFT点数,否则为帧数)</param>
        /// <param name="dataHeight">数据高度(移动方向的水平方向时此值为FFT点数,否则为帧数)</param>
        /// <param name="min">频谱图中的最小值</param>
        /// <param name="max">频谱图中的最大值</param>
        /// <param name="colormap">色域</param>
        /// <param name="moveOrientation">瀑布图移动方向</param>
        /// <param name="options">可选项参数</param>
        /// <exception cref="ArgumentOutOfRangeException">参数无效异常</exception>
        /// <exception cref="ArgumentException">参数无效异常</exception>
        public void Set(int dataWidth, int dataHeight, double min, double max, IColormap colormap, PlotMoveDirection moveOrientation, WaterfallPlotOptions options = null)
        {
            if (dataWidth < PlotConstant.SIZE_MIN)
            {
                throw new ArgumentOutOfRangeException(nameof(dataWidth), $"数据宽度不能小于{PlotConstant.SIZE_MIN}");
            }

            if (dataHeight < PlotConstant.SIZE_MIN)
            {
                throw new ArgumentOutOfRangeException(nameof(dataHeight), $"数据高度不能小于{PlotConstant.SIZE_MIN}");
            }

            if (!DoubleEx.Valid(min))
            {
                throw new ArgumentException("最小值无效", nameof(min));
            }

            if (!DoubleEx.Valid(max))
            {
                throw new ArgumentException("最大值无效", nameof(min));
            }

            if (min > max)
            {
                throw new ArgumentException("最小值不能大于最大值");
            }

            if (moveOrientation != PlotMoveDirection.Up &&
                moveOrientation != PlotMoveDirection.Down &&
                moveOrientation != PlotMoveDirection.Left &&
                moveOrientation != PlotMoveDirection.Right)
            {
                throw new NotSupportedException($"不支持的移动方向{moveOrientation.ToString()}");
            }

            this._dataRange = new ScottPlot.Range(min, max);


            if (options == null)
            {
                options = WaterfallPlotOptions.Default;
            }

            if (colormap == null)
            {
                colormap = new ScottPlot.Colormaps.Viridis();
            }
            this._colormap = colormap;

            this._dataHeight = dataHeight;
            this._dataWidth = dataWidth;

            this._imgPixs = dataHeight * dataWidth;
            this._argbsPointer.CheckSetLength(this._imgPixs * 2);
            this._argbsPointer.GetSpan().Fill(colormap.GetColor(min, this._dataRange).PremultipliedARGB);
            this._argsbWriteOffset = this._imgPixs;

            this._cacheOriginValue = options.CacheOriginValue;
            if (this._cacheOriginValue)
            {
                this._intensities.CheckSetLength(this._imgPixs);
                this._intensities.GetSpan().Fill(min);
            }
            else
            {
                this._intensities.Length = 0;
            }

            if (this._bitmap == null ||
                this._bitmap.Width != dataWidth ||
                this._bitmap.Height != dataHeight ||
                this._moveOrientation != moveOrientation ||
                this._options != null && (this._options.FlipVertically != options.FlipVertically || this._options.FlipHorizontally != options.FlipHorizontally))
            {
                if (this._bitmap != null)
                {
                    this._bitmap.Dispose();
                    this._bitmap2.Dispose();
                    this._bitmap2Canvas.Dispose();
                }

                this._imageInfo = new SKImageInfo(dataWidth, dataHeight);
                this._bitmap = new SKBitmap(this._imageInfo);

                this._bitmap2 = new SKBitmap(dataWidth, dataHeight);
                var bitmap2Canvas = new SKCanvas(this._bitmap2);
                float dx = dataWidth, dy = dataHeight;
                switch (moveOrientation)
                {
                    case PlotMoveDirection.Down:
                        if (!options.FlipHorizontally)
                        {
                            bitmap2Canvas.Scale(-1, 1);//水平翻转
                            bitmap2Canvas.Translate(-dx, 0);
                        }

                        bitmap2Canvas.Translate(dx, dy);
                        bitmap2Canvas.RotateDegrees(180);//旋转180度
                        break;
                    case PlotMoveDirection.Up:
                        if (options.FlipHorizontally)
                        {
                            bitmap2Canvas.Scale(-1, 1);//水平翻转
                            bitmap2Canvas.Translate(-dx, 0);
                        }

                        //默认向上,不做旋转操作
                        break;
                    case PlotMoveDirection.Left:
                        if (options.FlipVertically)
                        {
                            bitmap2Canvas.Scale(1, -1);//垂直翻转
                            bitmap2Canvas.Translate(0, -dy);
                        }

                        bitmap2Canvas.Translate(0, dx);
                        bitmap2Canvas.RotateDegrees(270);//旋转270度
                        break;
                    case PlotMoveDirection.Right:
                        if (options.FlipVertically)
                        {
                            bitmap2Canvas.Scale(1, -1);//垂直翻转
                            bitmap2Canvas.Translate(0, -dx);
                        }

                        bitmap2Canvas.Translate(dy, 0);
                        bitmap2Canvas.RotateDegrees(90);//旋转90度
                        break;
                    default:
                        throw new NotImplementedException($"未实现的移动方向{moveOrientation.ToString()}");
                }

                this._bitmap2Canvas = bitmap2Canvas;
            }


            if (moveOrientation == PlotMoveDirection.Up || moveOrientation == PlotMoveDirection.Down)
            {
                this._lessLineEleCount = (dataHeight - 1) * dataWidth;
            }
            else
            {
                this._lessLineEleCount = dataHeight * (dataWidth - 1);
            }

            if (this._paint != null)
            {
                if (options.Smooth && this._paint.FilterQuality != SKFilterQuality.High ||
                    !options.Smooth && this._paint.FilterQuality != SKFilterQuality.None)
                {
                    this._paint.Dispose();
                    this._paint = null;
                }
            }

            if (this._paint == null)
            {
                this._paint = new SKPaint()
                {
                    FilterQuality = options.Smooth ? SKFilterQuality.High : SKFilterQuality.None
                };
            }

            this._flipVertically = options.FlipVertically;
            this._flipHorizontally = options.FlipHorizontally;
            this._naNCellColor = options.NaNCellColor;
            this._alignedExtent = PlotHelper.GetAlignedExtent(options.AlignedExtentOptions, new CoordinateRect(0, dataWidth, 0, dataHeight), out this._cellWidth, out this._cellHeight);
            //this._alignedExtent = this.GetAlignedExtent(options, new CoordinateRect(0, dataWidth, 0, dataHeight), out this._cellWidth, out this._cellHeight);
            this._axisLimits = new AxisLimits(this._alignedExtent);

            this._moveOrientation = moveOrientation;
            this._options = options;
        }

        /*
        private CoordinateRect GetAlignedExtent(WaterfallPlotOptions options, CoordinateRect extentOrDefault, out double cellWidth, out double cellHeight)
        {
            CoordinateRect? originPosition = options.Position;
            if (options.CellWidth.HasValue || options.CellHeight.HasValue)
            {
                double left = extentOrDefault.Left;
                double bottom = extentOrDefault.Bottom;
                double right, top;

                if (options.CellWidth.HasValue)
                {
                    right = extentOrDefault.Left + options.CellWidth.Value * this._dataWidth;
                }
                else
                {
                    right = extentOrDefault.Right;
                }

                if (options.CellHeight.HasValue)
                {
                    top = extentOrDefault.Bottom + options.CellHeight.Value * this._dataHeight;
                }
                else
                {
                    top = extentOrDefault.Top;
                }
                originPosition = new CoordinateRect(left, right, bottom, top);
            }

            if (originPosition.HasValue)
            {
                CoordinateRect position = originPosition.Value;
                //user will provide the extends to the data. The image will be one cell wider and taller so we need to add that on (it is being added on in teh default case).
                double cellwidth = position.Width / (this._dataWidth - 1);
                double cellheight = position.Height / (this._dataHeight - 1);

                if (position.Left < position.Right)
                {
                    position.Right += cellwidth;
                }

                if (position.Left > position.Right)
                {
                    position.Left -= cellwidth; //cellwidth will be negative if extent is flipped
                }

                if (position.Bottom < position.Top)
                {
                    position.Top += cellheight;
                }

                if (position.Bottom > position.Top)
                {
                    position.Bottom -= cellheight; //cellheight will be negative if extent is inverted
                }

                extentOrDefault = position;
            }

            cellWidth = extentOrDefault.Width / this._dataWidth;
            cellHeight = extentOrDefault.Height / this._dataHeight;
            double xOffset = Math.Abs(cellWidth) * options.CellAlignment.HorizontalFraction();
            double yOffset = Math.Abs(cellHeight) * options.CellAlignment.VerticalFraction();
            var cellOffset = new Coordinates(-xOffset, -yOffset);
            return extentOrDefault.WithTranslation(cellOffset);
        }*/


 

        #region 获取图上指定坐标的值

        /// <summary>
        /// 获取图上指定坐标的值
        /// </summary>
        /// <param name="coordinates">坐标</param>
        /// <returns>值</returns>
        public double GetValue(Coordinates coordinates)
        {
            if (this._cacheOriginValue)
            {
                CoordinateRect rect = this._alignedExtent;
                if (!rect.Contains(coordinates))
                {
                    return double.NaN;
                }

                double distanceFromLeft = coordinates.X - rect.Left;
                int xIndex = (int)(distanceFromLeft / this._cellWidth);

                double distanceFromTop = rect.Top - coordinates.Y;
                int yIndex = (int)(distanceFromTop / this._cellHeight);

                int index;
                switch (this._moveOrientation)
                {
                    case PlotMoveDirection.Down:
                        //y相减
                        yIndex = this._dataHeight - yIndex;

                        if (this._flipHorizontally)
                        {
                            index = yIndex * this._dataWidth + (this._dataWidth - xIndex);
                        }
                        else
                        {
                            index = yIndex * this._dataWidth + xIndex;
                        }
                        break;
                    case PlotMoveDirection.Up:
                        if (this._flipHorizontally)
                        {
                            index = yIndex * this._dataWidth + (this._dataWidth - xIndex);
                        }
                        else
                        {
                            index = yIndex * this._dataWidth + xIndex;
                        }
                        break;
                    case PlotMoveDirection.Left:
                        int tmp1 = xIndex;
                        xIndex = yIndex;
                        yIndex = tmp1;

                        if (this._flipVertically)
                        {
                            index = yIndex * this._dataWidth + xIndex;
                        }
                        else
                        {
                            index = yIndex * this._dataWidth + (this._dataWidth - xIndex);
                        }

                        break;
                    case PlotMoveDirection.Right:
                        int tmp2 = xIndex;
                        xIndex = yIndex;
                        yIndex = this._dataHeight - tmp2;

                        if (this._flipVertically)
                        {
                            index = yIndex * this._dataWidth + (this._dataWidth - xIndex);
                        }
                        else
                        {
                            index = yIndex * this._dataWidth + xIndex;
                        }
                        break;
                    default:
                        throw new NotImplementedException($"未实现的移动方向{this._moveOrientation.ToString()}");
                }

                if (index < 0 || index > this._intensities.Length)
                {
                    return double.NaN;
                }
                else
                {
                    return this._intensities[index];
                }
            }
            else
            {
                throw new InvalidOperationException("选项中未启用缓存原始值功能，根据坐标获取原始值操作失败");
            }
        }

        #endregion



        #region IPlottable

        private bool _isVisible = true;
        /// <summary>
        /// 是否可见
        /// </summary>
        public bool IsVisible
        {
            get { return this._isVisible; }
            set { this._isVisible = value; }
        }


        private IAxes _axes = new Axes();
        /// <summary>
        /// 获取或设置坐标系
        /// </summary>
        public IAxes Axes
        {
            get { return this._axes; }
            set { this._axes = value; }
        }

        private AxisLimits _axisLimits;
        /// <summary>
        /// 获取坐标范围
        /// </summary>
        /// <returns></returns>
        public AxisLimits GetAxisLimits()
        {
            return this._axisLimits;
        }

        /// <summary>
        /// 渲染图
        /// </summary>
        /// <param name="rp"></param>
        public void Render(RenderPack rp)
        {
            if (this._bitmap == null)
            {
                return;
            }

            if (this._argbsIsUpdated)
            {
                this._argbsIsUpdated = false;
                SpanZ<uint> argbs = this._argbsPointer.GetSpanZ();
                this._bitmap.InstallPixels(this._imageInfo, new IntPtr(argbs.DataPtr + this._argsbWriteOffset - this._imgPixs), this._imageInfo.RowBytes, null, null);
                this._bitmap2Canvas.DrawBitmap(this._bitmap, 0, 0, this._paint);
            }

            SKRect rect = this._axes.GetPixelRect(this._alignedExtent).ToSKRect();
            rp.Canvas.DrawBitmap(this._bitmap2, rect, this._paint);
        }

        /// <summary>
        /// 
        /// </summary>
        public IEnumerable<LegendItem> LegendItems
        {
            get { return Enumerable.Empty<LegendItem>(); }
        }

        #endregion

    }

}
